#pragma once

#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include "thread.hpp"
#include <unistd.h>
#include "lockGuard.hpp"

const int threadnum_ = 3;

template <typename T>
class ThreadPool
{
private:
    int num_;
    std::vector<Thread *> threads_;
    std::queue<T> taskqueue_;
    pthread_mutex_t lock;
    pthread_cond_t cond;

public:
    ThreadPool(int threadnum = threadnum_) : num_(threadnum)
    {
        pthread_mutex_init(&lock, NULL);
        pthread_cond_init(&cond, NULL);
        for (int i = 1; i <= num_; i++)
        {
            threads_.push_back(new Thread(i, &ThreadPool::threadFunc, this));
        }
    }

    void run()
    {
        for (auto &thread : threads_)
        {
            thread->start();
        }
    }

    pthread_mutex_t *getLock()
    {
        return &lock;
    }

    T getTask()
    {
        T t = taskqueue_.front();
        taskqueue_.pop();
        return t;
    }

    static void *threadFunc(void *args)
    {
        ThreadData *td = (ThreadData *)args;
        ThreadPool<T> *tp = (ThreadPool<T>*)td->args_;
        while (true)
        {
            T task;
            {
                lockGuard lockguard(tp->getLock());
                while (tp->isEmpty())
                {
                    tp->waitCond();
                }
                task = tp->getTask();
            }

            // std::cout<<td->name_<<" is working"<<std::endl;
            // sleep(1);
            task(td->name_);
        }
    }

    // void joins()
    // {
    //     for(auto &thread : threads_)
    //     {
    //         thread->join();
    //     }
    // }

    bool isEmpty()
    {
        return taskqueue_.empty();
    }

    void waitCond()
    {
        pthread_cond_wait(&cond, &lock);
    }

    void pushTask(const T &task)
    {
        lockGuard lock(&lock);
        taskqueue_.push(task);
        pthread_cond_signal(&cond);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&cond);
        for (auto &thread : threads_)
        {
            thread->join();
            delete thread;
        }
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&cond);
    }
};